﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Authentication;
using System.Text;
using System.Threading;

namespace Test
{
    class Program
    {
        public static readonly DateTime BaseTimeT = new DateTime(1970, 1, 1, 0, 0, 0);

        static B2H ParseB2H(byte[] buffer, int offset)
        {
            return
                new B2H
                {
                    MagicValue = BitConverter.ToUInt32(buffer, offset + 0),
                    MessageType = buffer[offset + 4],
                    StreamId = buffer[offset + 5],
                    ExtLength = buffer[offset + 6],
                    Rsvd = buffer[offset + 7],
                    Length = BitConverter.ToInt32(buffer, offset + 8)
                };
        }
        static B2H ParseB2H(Stream stream, int offset)
        {
            stream.Seek(offset, SeekOrigin.Begin);
            var buffer = new byte[12];
            stream.Read(buffer, 0, 12);
            return ParseB2H(buffer, 0);
        }

        static void Main(string[] args)
        {
            var cfg =
                new ConnectionConfig()
                {
                    //Host = "172.16.17.97",
                    Host = "192.168.1.3",
                    Port = 6002,
                    Username = "Admin",
                    Password = "123456"
                };
            var c = new Connector();
            Exception ex;
            if (!c.Connect(cfg, out ex))
            {
                Console.WriteLine(ex.Message);
                return;
            }
            while (Console.ReadKey().Key != ConsoleKey.Escape)
            {

            }
            if (!c.Disconnect(out ex))
            {
                Console.WriteLine(ex.Message);
                Console.ReadLine();
            }
            Console.WriteLine();
            Console.WriteLine("Stopped...");
            return;





            var req = new byte[] {
0x61, 0x64, 0x6d, 0x69, 0x6e, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Connect("172.16.17.79", 6002);
            try
            {
                socket.Send(req);
                var buff = new byte[0xFFFFFF];
                var h = new Checker(0x00, 0x00, 0x01, 0xB2);
                var list1 = new List<byte>();
                var shown = false;
                while (true)
                {
                    if (socket.Available <= 0)
                    {
                        Thread.Sleep(100);
                        continue;
                    }
                    var len = socket.Receive(buff, 0, 1, SocketFlags.None);
                    if (len <= 0) continue;
                    if (!shown) list1.Add(buff[0]);
                    if (!h.Check(buff[0])) continue;

                    if (!shown)
                    {
                        Dump(list1.ToArray(), 0, list1.Count);
                        list1.Clear();
                        shown = true;
                        Console.ReadLine();
                    }
                    socket.Receive(buff, 0, 40, SocketFlags.None);

                    ColorOutput(buff, 0x00, 0x01, ConsoleColor.DarkBlue, ConsoleColor.White);
                    ColorOutput(buff, 0x01, 0x01, ConsoleColor.DarkGreen, ConsoleColor.Black);
                    ColorOutput(buff, 0x02, 0x01, ConsoleColor.DarkCyan, ConsoleColor.Black);
                    ColorOutput(buff, 0x03, 0x01, ConsoleColor.DarkRed, ConsoleColor.Black);
                    ColorOutput(buff, 0x04, 0x04, ConsoleColor.DarkMagenta, ConsoleColor.Black);
                    ColorOutput(buff, 0x08, 0x04, ConsoleColor.DarkYellow, ConsoleColor.Black);
                    ColorOutput(buff, 0x0C, 0x01, ConsoleColor.Gray, ConsoleColor.Black);
                    ColorOutput(buff, 0x0D, 0x01, ConsoleColor.DarkGray, ConsoleColor.Black);
                    ColorOutput(buff, 0x0E, 0x01, ConsoleColor.Blue, ConsoleColor.White);
                    ColorOutput(buff, 0x0F, 0x01, ConsoleColor.Green, ConsoleColor.Black);
                    ColorOutput(buff, 0x10, 0x04, ConsoleColor.Cyan, ConsoleColor.Black);
                    ColorOutput(buff, 0x14, 0x01, ConsoleColor.Red, ConsoleColor.Black);
                    ColorOutput(buff, 0x15, 0x01, ConsoleColor.Magenta, ConsoleColor.Black);
                    ColorOutput(buff, 0x16, 0x01, ConsoleColor.Yellow, ConsoleColor.Black);
                    ColorOutput(buff, 0x17, 0x01, ConsoleColor.White, ConsoleColor.Black);
                    ColorOutput(buff, 0x18, 0x08, ConsoleColor.DarkBlue, ConsoleColor.White);
                    ColorOutput(buff, 0x20, 0x06, ConsoleColor.DarkGreen, ConsoleColor.Black);
                    ColorOutput(buff, 0x26, 0x02, ConsoleColor.DarkCyan, ConsoleColor.Black);
                    //Dump2(buff, 0, 38);
                    //Console.WriteLine(len);
                }
            }
            finally
            {
                socket.Disconnect(false);
                socket.Close();
            }


            return;
            const string path = @"C:\Users\Ben.Hsieh\Pictures\edge.raw";
            var b0 = new Checker(0x00, 0x00, 0x01, 0xB0);
            var b1 = new Checker(0x00, 0x00, 0x01, 0xB1);
            var b2 = new Checker(0x00, 0x00, 0x01, 0xB2);
            var b3 = new Checker(0x00, 0x00, 0x01, 0xB3);
            var b4 = new Checker(0x00, 0x00, 0x01, 0xB4);
            var b5 = new Checker(0x00, 0x00, 0x01, 0xB5);
            var b6 = new Checker(0x00, 0x00, 0x01, 0xB6);
            var list = new List<string>();
            var buffer = new byte[8];
            var b2d = new byte[32];
            using (var f = File.OpenRead(path))
            {
                while (true)
                {
                    list.Clear();
                    var n = f.ReadByte();
                    if (n < 0) break;
                    var b = (byte)n;
                    list.Add(f.Position.ToString("X8"));
                    if (b0.Check(b)) list.Add(" B0"); //Console.Write(@"B0 ");
                    if (b1.Check(b)) list.Add(" B1"); //Console.Write(@"B1 ");
                    if (b2.Check(b)) list.Add(" B2"); //Console.Write(@"B2 ");
                    if (b3.Check(b)) list.Add(" B3"); //Console.Write(@"B3 ");
                    if (b4.Check(b)) list.Add(" B4"); //Console.Write(@"B4 ");
                    if (b5.Check(b)) list.Add(" B5"); //Console.Write(@"B5 ");
                    if (b6.Check(b)) list.Add(" B6"); //Console.Write(@"B6 ");
                    if (list.Count < 2) continue;
                    f.Read(buffer, 0, buffer.Length);
                    for (var j = 0; j < 4; j++)
                        list.Add(" " + buffer[j].ToString("X2"));
                    list.Add(" " + BitConverter.ToInt32(buffer, 4).ToString("X8"));
                    f.Read(b2d, 0, b2d.Length);
                    Console.WriteLine();
                    //Console.WriteLine(string.Join("", list.ToArray()));
                    DumpB2D(b2d);
                }
            }
            //Console.ReadLine();
        }
        static void ColorOutput(byte[] buffer, int offset, int count, ConsoleColor fg, ConsoleColor bg)
        {
            var currentF = Console.ForegroundColor;
            var currentB = Console.BackgroundColor;
            Console.ForegroundColor = fg;
            Console.BackgroundColor = bg;
            for (var i = 0; i < count; i++)
            {
                Console.Write(buffer[offset + i].ToString("X2"));
            }
            Console.ForegroundColor = currentF;
            Console.BackgroundColor = currentB;
        }
        static void Dump2(byte[] buffer, int offset, int count)
        {
            for (var i = 0; i < count; i++)
                Console.Write(buffer[offset + i].ToString("X2"));
            Console.WriteLine();
        }
        static void DumpB2D(byte[] b2d)
        {
            ColorOutput(b2d, 0, 4, ConsoleColor.White, ConsoleColor.Black);
            ColorOutput(b2d, 4, 1, ConsoleColor.DarkBlue, ConsoleColor.Black);
            ColorOutput(b2d, 5, 1, ConsoleColor.DarkGreen, ConsoleColor.Black);
            ColorOutput(b2d, 6, 1, ConsoleColor.DarkCyan, ConsoleColor.Black);
            ColorOutput(b2d, 7, 1, ConsoleColor.DarkRed, ConsoleColor.Black);
            ColorOutput(b2d, 8, 4, ConsoleColor.DarkMagenta, ConsoleColor.Black);
            ColorOutput(b2d, 12, 1, ConsoleColor.DarkYellow, ConsoleColor.Black);
            ColorOutput(b2d, 13, 1, ConsoleColor.Gray, ConsoleColor.Black);
            ColorOutput(b2d, 14, 1, ConsoleColor.DarkGray, ConsoleColor.Black);
            ColorOutput(b2d, 15, 1, ConsoleColor.Blue, ConsoleColor.Black);
            ColorOutput(b2d, 16, 8, ConsoleColor.Green, ConsoleColor.Black);
            ColorOutput(b2d, 24, 6, ConsoleColor.Cyan, ConsoleColor.Black);
            ColorOutput(b2d, 30, 2, ConsoleColor.Red, ConsoleColor.Black);
        }
        static void Dump(byte[] buffer, int offset, int count)
        {
            Console.WriteLine("         00 01 02 03 04 05 06 07 - 08 09 0A 0B 0C 0D 0E 0F");
            Console.WriteLine("======== == == == == == == == ==   == == == == == == == ==");
            for (var i = 0; i < count; i++)
            {
                if ((i & 0x0F) == 0x00) Console.Write((offset + i).ToString("X8") + " ");
                Console.Write(buffer[offset + i].ToString("X2") + " ");
                if ((i & 0x07) == 0x07) Console.Write("  ");
                if ((i & 0x0F) == 0x0F) Console.WriteLine();
            }
            Console.WriteLine();
            Console.WriteLine("======== == == == == == == == ==   == == == == == == == ==");
        }

        private static string HttpRequest(string url)
        {
            var uri = new Uri(url);
            var command = Encoding.Default.GetBytes(string.Format("GET {0} HTTP/1.1\r\n\r\n", uri.PathAndQuery));
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Connect(uri.Host, uri.Port);
            socket.Send(command);
            var header = new List<string>();
            var tmp = new List<byte>();
            var buffer = new byte[8192];
            int len;
            var mem = new MemoryStream();
            Action<byte> a;
            Action<byte> d = mem.WriteByte;
            Action<byte> h =
                b =>
                {
                    if (b == '\r') return;
                    if (b == '\n')
                    {
                        if (tmp.Count < 1)
                        {
                            a = d;
                            return;
                        }
                        var line = Encoding.ASCII.GetString(tmp.ToArray());
                        header.Add(line);
                        tmp.Clear();
                        return;
                    }
                    tmp.Add(b);
                };
            a = h;
            do
            {
                len = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
                for (var i = 0; i < len; i++)
                    a(buffer[i]);
            } while (len > 0);
            socket.Close();
            return Encoding.UTF8.GetString(mem.ToArray());
        }

        private static void Test()
        {
            while (true)
            {
                var command = Console.ReadLine();
                if (string.IsNullOrEmpty(command)) continue;
                int count;
                if (int.TryParse(command, out count))
                {
                    Console.WriteLine("Test1:");
                    Test1(count);
                    Console.WriteLine("Test2:");
                    Test2(count);
                }
                var cmd = command.ToLower();
                if (cmd == "exit" || cmd == "quit") break;
            }
        }
        private static void Test1(int count)
        {
            var buffer = new byte[12];
            var watch = new Stopwatch();
            watch.Start();
            for (var i = 0; i < count; i++)
            {
                var b2f = RawData.Deserialize<B2H>(buffer);
            }
            watch.Stop();
            Console.WriteLine(watch.Elapsed);
        }
        private static void Test2(int count)
        {
            var buffer = new byte[12];
            var watch = new Stopwatch();
            watch.Start();
            for (var i = 0; i < count; i++)
            {
                var b2h = new B2H();
                b2h.MagicValue = BitConverter.ToUInt32(buffer, 0);
                b2h.MessageType = buffer[4];
                b2h.StreamId = buffer[5];
                b2h.ExtLength = buffer[6];
                b2h.Rsvd = buffer[7];
                b2h.Length = BitConverter.ToInt32(buffer, 8);
            }
            watch.Stop();
            Console.WriteLine(watch.Elapsed);
        }
    }


    public enum B2MessageType : byte
    {
        VIDEO_MPEG4 = 0x01,
        AUDIO_PCM8K = 0x02,
        AUDIO_PCM8K_Timestamp = 0x03,
        VIDEO_MJPEG = 0x04,
        VIDEO_H264 = 0x05,
        AUDIO_G711A = 0x06,
        AUDIO_G711U = 0x07,

    }
    [StructLayout(LayoutKind.Sequential)]
    public struct B2H
    {
        public uint MagicValue;
        public byte MessageType;
        public byte StreamId;
        public byte ExtLength;
        public byte Rsvd;
        public int Length;
    }
    [StructLayoutAttribute(LayoutKind.Sequential)]
    public struct B2D
    {
        /// <summary>time_t</summary>
        public uint Time;
        public byte TimeZone;
        /// <summary>0: video loss, 1: video ok</summary>
        public byte VideoLoss;
        /// <summary>0x02: motion1, 0x04: motion 2, 0x08: motion 3</summary>
        public byte Motion;
        /// <summary>0: triggered, 1: not triggered</summary>
        public byte DigitalInput;
        public int FrameCounter;
        public byte Resolution;
        public byte Bitrate;
        /// <summary>0: mode1, 1:mode2</summary>
        public byte FpsMode;
        /// <summary>
        /// In constant FPS mode, it indicates the video server's constant FPS number. In variable FPS mode, in indicates the variable FPS number which was requested by the TCP host. If it is not in TCP, it indicates the variable FPS number
        /// </summary>
        public byte Fps;

        public uint Reserved1;
        public uint Reserved2;
        public uint Reserved3;
        public uint Reserved4;
    }
    [StructLayoutAttribute(LayoutKind.Sequential)]
    public struct RawFileHeader
    {
        /// <summary>000001B2</summary>
        public uint MagicValue;
        /// <summary>11: TCP-1.0; 22: TCP-2.0;</summary>
        public uint StreamType;
        /// <summary>11: ISO 14496; 22: ...</summary>
        public uint VideoType;
        /// <summary>00: None; 11: PCM; ...</summary>
        public uint AudioType;
        /// <summary>00: None; 11: TCP-2.0;</summary>
        public uint ControlType;
        /// <summary>Reserve</summary>
        public uint Audio2Type;
        /// <summary>Reserve</summary>
        public uint Control2Type;
        /// <summary>0 - 15</summary>
        public uint BiteRate;
        /// <summary>FPS</summary>
        public uint FPS;
        /// <summary>0 - 5</summary>
        public uint Resolution;

        public uint Reserve1;
        public uint Reserve2;
        public uint Reserve3;
        public uint Reserve4;
        public uint Reserve5;
        public uint Reserve6;
    }

    [StructLayoutAttribute(LayoutKind.Sequential)]
    public struct RawFileTail
    {
        /// <summary>0x000001B2</summary>
        public uint MagicValue;
        /// <summary>0xAC710517</summary>
        public uint Header;
        /// <summary>0x01000001</summary>
        public uint Version;
        public uint BeginTime;
        public uint EndTime;
        public uint GOP;
        public uint GOPSize;
        public uint FPS;
        public uint Width;
        public uint Height;
        public uint FrameCount;
        /// <summary>Timezone</summary>
        public uint Reserve;
        public uint ChecksumMethod;
        public uint Checksum;
    }


    public class RawFile
    {
        private readonly List<long> _index;
        public long[] Offsets { get { return _index.ToArray(); } }
        public RawFileHeader Header { get; private set; }
        public RawFileTail Tail { get; private set; }
        public string FileName { get; private set; }

        public RawFile(string fileName)
        {
            FileName = fileName;
            _index = new List<long>();
            Refresh();
        }
        public void Refresh()
        {
            var buffer = new byte[64];
            using (var f = File.OpenRead(FileName))
            {
                f.Read(buffer, 0, 64);
                Header = RawData.Deserialize<RawFileHeader>(buffer);
                while (f.Position < f.Length - 56)
                {
                    f.Read(buffer, 0, 12);
                    var len = BitConverter.ToInt32(buffer, 8);
                    _index.Add(f.Position - 12);
                    f.Seek(len, SeekOrigin.Current);
                }
                f.Read(buffer, 0, 56);
                Tail = RawData.Deserialize<RawFileTail>(buffer);
            }
        }
    }
    public static class RawData
    {
        public static byte[] Serialize(object obj)
        {
            var rawSize = Marshal.SizeOf(obj);
            var buffer = Marshal.AllocHGlobal(rawSize);
            Marshal.StructureToPtr(obj, buffer, true);
            var result = new byte[rawSize];
            Marshal.Copy(buffer, result, 0, rawSize);
            Marshal.FreeHGlobal(buffer);
            return result;
        }
        public static T Deserialize<T>(byte[] rawData)
        {
            var t = typeof(T);
            var rawSize = Marshal.SizeOf(t);
            var buffer = Marshal.AllocHGlobal(rawSize);
            Marshal.Copy(rawData, 0, buffer, rawSize);
            var result = Marshal.PtrToStructure(buffer, t);
            Marshal.FreeHGlobal(buffer);
            return (T)result;
        }
        public static T Deserialize<T>(IntPtr source)
        {
            var t = typeof(T);
            return (T)Marshal.PtrToStructure(source, t);
        }
    }

    public class Connector
    {
        private Socket _socket;
        private readonly List<object> _keepRunning;
        public ConnectionConfig Config { get; private set; }
        public bool IsConnected { get; private set; }
        public Connector()
        {
            _keepRunning = new List<object>();
        }
        public bool Connect(ConnectionConfig config, out Exception exception)
        {
            exception = null;
            if (IsConnected)
            {
                exception = new InvalidOperationException("Connection has been established");
                return false;
            }

            Config = config;
            try
            {
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _socket.Connect(Config.Host, Config.Port);
                var buffer = new byte[128];
                var received = 0;
                Encoding.ASCII.GetBytes(Config.Username).CopyTo(buffer, 0);
                Encoding.ASCII.GetBytes(Config.Password).CopyTo(buffer, 64);
                _socket.Send(buffer, 0, 128, SocketFlags.None);
                var expired = DateTime.Now.AddSeconds(30);
                while (expired > DateTime.Now && received < buffer.Length)
                {
                    if (_socket.Available < 1)
                    {
                        Thread.Sleep(100);
                        continue;
                    }
                    var offset = received;
                    var count = buffer.Length - received;
                    received += _socket.Receive(buffer, offset, count, SocketFlags.None);
                }
                if (received != buffer.Length)
                {
                    exception = new Exception("Invalid device!");
                    return false;
                }
                switch (buffer[0])
                {
                    case 0:
                        new Thread(Receive) { IsBackground = true }.Start();
                        IsConnected = true;
                        return true;
                    case 1:
                        exception = new AuthenticationException("authentication fail");
                        return false;
                    case 2:
                        exception = new Exception("STREAM ID ERROR!");
                        return false;
                    default:
                        exception = new Exception("Invalid device!");
                        return false;
                }
            }
            catch (Exception ex)
            {
                IsConnected = false;
                exception = ex;
                return false;
            }
        }
        private static void Receive(Socket socket, byte[] buffer, int offset, int length, TimeSpan timeout)
        {
            var received = 0;
            var expired = DateTime.Now.Add(timeout);
            while (expired >= DateTime.Now && received < length)
            {
                if (socket.Available < 1)
                {
                    Thread.Sleep(100);
                    continue;
                }
                var start = offset + received;
                var len = length - received;
                received += socket.Receive(buffer, start, len, SocketFlags.None);
                expired = DateTime.Now.Add(timeout);
            }
            if (received < length) throw new TimeoutException();
        }
        private void Receive()
        {
            var buffer = new byte[0xFFFFFF];
            _keepRunning.Add(1);
            var fn = 0;
            while (_keepRunning.Count > 0)
            {
                try
                {
                    Receive(_socket, buffer, 0, 12, TimeSpan.FromSeconds(30));
                    var length = BitConverter.ToInt32(buffer, 8);
                    Receive(_socket, buffer, 12, length, TimeSpan.FromSeconds(30));
                    //Dump(fn.ToString("X4")+".TXT", buffer, 0, length+12);
                    DumpFrame(buffer, 0);
                    fn++;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            IsConnected = false;
        }

        private static DateTime BaseTime = new DateTime(1970, 1, 1);
        static void DumpFrame(byte[] buffer, int offset)
        {
            var length = BitConverter.ToInt32(buffer, offset + 8);
            var time = BaseTime.AddSeconds(BitConverter.ToInt32(buffer, offset + 12 + 0));
            var timezone = BitConverter.ToInt32(buffer, offset + 12 + 4);
            var videoLoss = buffer[offset + 12 + 5] == 0;
            var md = buffer[offset + 12 + 6];
            var list = new List<string>();
            if ((md & 0x02) == 0x02) list.Add("1");
            if ((md & 0x04) == 0x04) list.Add("2");
            if ((md & 0x08) == 0x08) list.Add("3");

            var motion = string.Join(", ", list.ToArray());
            if (!string.IsNullOrEmpty(motion)) motion = "MD(" + motion + ") ";
            var di = buffer[offset + 12 + 7];
            list.Clear();
            if ((di & 0x01) == 0x00) list.Add("1");
            if ((di & 0x02) == 0x00) list.Add("2");
            var dis = string.Join(", ", list.ToArray());
            if (!string.IsNullOrEmpty(dis)) dis = "DI(" + dis + ") ";
            var frameCount = BitConverter.ToInt32(buffer, offset + 12 + 8);
            var resolution = buffer[offset + 12 + 12];
            var bitrate = buffer[offset + 12 + 13];
            var fpsMode = buffer[offset + 12 + 14];
            var fpsNum = buffer[offset + 12 + 15];
            var fps = string.Format("FPS({0}, {1}) ", fpsNum, fpsMode < 1 ? "C" : "V");
            var timestamp = BaseTime
                .AddSeconds(BitConverter.ToInt32(buffer, offset + 12 + 16))
                .AddMilliseconds(BitConverter.ToInt32(buffer, offset + 12 + 20) / 1000.0);
            var md1 = BitConverter.ToInt16(buffer, offset + 12 + 24);
            var md2 = BitConverter.ToInt16(buffer, offset + 12 + 26);
            var md3 = BitConverter.ToInt16(buffer, offset + 12 + 28);
            var frameType = "";
            //"buffer[offset + 12 + 32 + 3] == 0xB6 ? "P" : "I";

            switch (buffer[4])
            {
                case 0x01:
                    frameType += "MPEG4-" + (buffer[offset + 44 + 3] == 0xB6 ? "P" : "I");
                    break;
                case 0x05:
                    frameType += "H.264-" + (buffer[offset + 44 + 4] != 0x67 ? "P" : "I");
                    break;
                case 0x04: frameType += "M-JPEG"; break;
                case 0x02: frameType += "AUDIO_8KPCM"; break;
                case 0x03: frameType += "AUDIO_8KPCM_TS"; break;
                case 0x06: frameType += "AUDIO_G711A"; break;
                case 0x07: frameType += "AUDIO_G711U"; break;
                default: frameType += "Unknown"; break;
            }
            Console.WriteLine("{0:X8} {1:HH:mm:ss.fff} {2} {6:X6} {3}{4}{5}", frameCount, timestamp, frameType, motion, fps, dis, length);
        }
        static void Dump(string fileName, byte[] buffer, int offset, int count)
        {
            using (var w = new StreamWriter(fileName))
            {
                w.WriteLine("         00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
                w.WriteLine("======== ===============================================");
                for (var i = 0; i < count; i++)
                {
                    if ((i & 0x0F) == 0x00) w.Write((offset + i).ToString("X8") + " ");
                    w.Write(buffer[offset + i].ToString("X2") + " ");
                    //if ((i & 0x07) == 0x07) w.Write("  ");
                    if ((i & 0x0F) == 0x0F) w.WriteLine();
                }
                w.WriteLine();
                w.WriteLine("======== ===============================================");
            }
        }
        public bool Disconnect(out Exception exception)
        {
            exception = null;
            if (!IsConnected)
            {
                exception = new InvalidOperationException("Connection has not been established");
                return false;
            }
            try
            {
                _keepRunning.Clear();
                while (_keepRunning.Count > 0)
                {
                    Thread.Sleep(200);
                }
                _socket.Disconnect(false);
                _socket.Close();
                return true;
            }
            catch (Exception ex)
            {
                exception = ex;
                return false;
            }
        }
    }
    public struct ConnectionConfig
    {
        public string Host;
        public int Port;
        public string Username;
        public string Password;
    }
    public class EventArgs<T> : EventArgs
    {
        public T Data { get; private set; }
        public EventArgs(T data)
        {
            Data = data;
        }
    }
    public class Checker
    {
        private readonly byte[] _pattern;
        private readonly List<byte> _list;
        public Checker(params byte[] pattern)
        {
            _pattern = pattern;
            _list = new List<byte>();
        }
        public bool Check(byte value)
        {
            _list.Add(value);
            if (_list.Count < _pattern.Length) return false;
            for (var i = 0; i < _list.Count; i++)
            {
                if (_list[i] == _pattern[i]) continue;
                _list.RemoveAt(0);
                return false;
            }
            _list.Clear();
            return true;
        }
    }
    class Camera
    {
        private readonly Encoding _enc = Encoding.ASCII;
        public byte[] AuthRequest(string username, string password)
        {
            var buffer = new byte[128];
            _enc.GetBytes(username).CopyTo(buffer, 0);
            _enc.GetBytes(password).CopyTo(buffer, 64);
            return buffer;
        }
        public void ParseAuthReply(byte[] buffer, out bool success, out string cameraName)
        {
            success = buffer[0] == 0;
            cameraName = _enc.GetString(buffer, 10, 32).Replace("\0", "");
        }
    }
}

